#define _CRT_SECURE_NO_WARNINGS

#include <stdbool.h>
#include <string.h>
#include <stdio.h>

#include "scanner.h"

typedef struct{
    const char *start;
    const char *current;
    int line;
}Scanner;

static Scanner scanner;
static char meesage[50];

void initScanner(const char *source)
{
    scanner.start = source;
    scanner.current = source;
    scanner.line = 1;
}

static bool isAlpha(char c)
{
    return (c >= 'a' && c <= 'z')||
        (c >= 'A' && c <= 'Z')||
        c == '_';
}

static bool isDigit(char c)
{
    return c >= '0' && c <= '9';
}

static bool isAtEnd()
{
    return *scanner.current == '\0';
}

static char advance()
{
    return *scanner.current++;
}

static char peek()
{
    return *scanner.current;
}   

static char peekNext()
{
    if(isAtEnd())
    {
        return '\0';
    }
    return *(scanner.current +1);
}
// 判断当前正在处理的字符是否符合预期，符合curr前进一步
static bool match(char expected)
{
    if(isAtEnd())
    {
        return false;
    }
    if(peek() != expected)
    {
        return false;
    }
    scanner.current++;
    return true;
}

static Token makeToken(TokenType type)
{
    Token token;
    token.type = type;
    token.start = scanner.start;
    token.length = (int)(scanner.current - scanner.start);
    token.line = scanner.line;

    return token;
}

static Token errorToken(const char *message)
{
    Token token;
    token.type = TOKEN_ERROR;
    token.start = message;
    token.length = (int)strlen(message);
    token.line = scanner.line;

    return token;
}
// 跳过
static void skipWhitespace()
{
    for(;;)
    {
        char c = peek();
        switch(c)
        {
        case ' ':
        case '\r':
        case '\t':
            advance();
            break;
        case '\n':
            scanner.line++;
            advance();
            break;
        case '/':
            if(peekNext() == '/')
            {
                while(!isAtEnd() && peek() != '\n')
                {
                    advance();
                }
            }else
            {
                return;
            }
            break;
        default:
            return;
        }
    }
}


static TokenType checkKeyWord(int start, int length, const char*rest, TokenType type)
{
    if(scanner.current - scanner.start == start+length && 
       memcmp(scanner.start + start, rest, length) == 0)
    {
        return type;
    }
    return TOKEN_IDENTIFIER;
}

static TokenType identifierType()
{
    // 确定identifier类型主要有两种方式：
    // 1. 将所有的关键字放入哈希表中，然后查表确认
    // Key-Value 就是"关键字-TokenType" 可以做 但存在额外内存占用且效率不如下一个方式好
    // 2. 将所有的关键字放入Trie树(字典查找树)中，然后查表确认
    // Trie树的方式不管是空间上还是时间上都优于哈希表的方式
    // 用switch...switch...if组合构建逻辑上的trie树
    char first = scanner.start[0];	// 该Token的第一个字符
    switch (first) {
    case 'b':return checkKeyWord(1, 4, "reak", TOKEN_BREAK);
    case 'c':
             {
                 int len = scanner.current - scanner.start;
                 if (len > 1)
                 {
                     switch (scanner.start[1])
                     {
                     case 'a': return checkKeyWord(2, 2, "se", TOKEN_CASE);
                     case 'h': return checkKeyWord(2, 2, "ar", TOKEN_CHAR);
                     case 'o':
                               if (len > 3 && scanner.start[2] == 'n')
                               {
                                   switch (scanner.start[3])
                                   {
                                   case 's': return checkKeyWord(4, 1, "t", TOKEN_CONST);
                                   case 't': return checkKeyWord(4, 4, "inue", TOKEN_CONTINUE);
                                   }
                               }
                     }
                 }
                 break;
             }
    case 'd':
             {
                 int len = scanner.current - scanner.start;
                 if (len > 1)
                 {
                     switch (scanner.start[1])
                     {
                     case 'e': return checkKeyWord(2, 5, "fault", TOKEN_DEFAULT);
                     case 'o': return checkKeyWord(2, 4, "uble", TOKEN_DOUBLE);
                     }
                 }
                 break;
             }
    case 'f':
             {
                 int len = scanner.current - scanner.start;
                 if (len > 1)
                 {
                     switch (scanner.start[1])
                     {
                     case 'l': return checkKeyWord(2, 3, "oat", TOKEN_FLOAT);
                     case 'o': return checkKeyWord(2, 1, "r", TOKEN_FOR);
                     }
                 }
                 break;
             }
    case 'g': return checkKeyWord(1, 3, "oto", TOKEN_GOTO);
    case 'i':
              {
                  int len = scanner.current - scanner.start;
                  if (len > 1)
                  {
                      switch (scanner.start[1])
                      {
                      case 'f': return checkKeyWord(2, 0, "", TOKEN_IF);
                      case 'n': return checkKeyWord(2, 1, "t", TOKEN_INT);
                      }
                  }
                  break;
              }
    case 'l': return checkKeyWord(1, 3, "ong", TOKEN_LONG);
    case 'r': return checkKeyWord(1, 5, "eturn", TOKEN_RETURN);
    case 's': {
                  int len = scanner.current - scanner.start;
                  if (len > 1) {
                      switch (scanner.start[1]) {
                      case 'h': return checkKeyWord(2, 3, "ort", TOKEN_SHORT);
                      case 'i':
                                if (len > 2) {
                                    switch (scanner.start[2]) {
                                    case 'g': return checkKeyWord(3, 3, "ned", TOKEN_SIGNED);
                                    case 'z': return checkKeyWord(3, 3, "eof", TOKEN_SIZEOF);
                                    }
                                }
                                break;
                      case 't': return checkKeyWord(2, 4, "ruct", TOKEN_STRUCT);
                      case 'w': return checkKeyWord(2, 4, "itch", TOKEN_SWITCH);
                      }
                  }
                  break;
              }
    case 't': return checkKeyWord(1, 6, "ypedef", TOKEN_TYPEDEF);
    case 'u': {
                  int len = scanner.current - scanner.start;
                  if (len > 2 && scanner.start[1] == 'n') {
                      switch (scanner.start[2]) {
                      case 'i': return checkKeyWord(3, 2, "on", TOKEN_UNION);
                      case 's': return checkKeyWord(3, 5, "igned", TOKEN_UNSIGNED);
                      }
                  }
                  break;
              }
    case 'v': return checkKeyWord(1, 3, "oid", TOKEN_VOID);
    case 'w': return checkKeyWord(1, 4, "hile", TOKEN_WHILE);
              // TODO
    }
    // 没有进switch一定是标识符
    return TOKEN_IDENTIFIER;
}
// 当前Token的开头是下划线或字母判断它是不是标识符Token
static Token identifier()
{
    while(isAlpha(peek() || isDigit(peek())))
    {
        advance();
    }

    return makeToken(identifierType());
}

static Token number()
{
    while(isDigit(peek())) advance();

    if(peek() == '.' && isDigit(peekNext()))
    {
        advance();
        while(isDigit(peek())) advance();
    }

    return makeToken(TOKEN_NUMBER);
}

static Token string()
{
    while(!isAtEnd()  && peek() != '"')
    {
        if(peek() == '\n')
        {
            return errorToken("不支持多行字符串.");
        }
        advance();
    }
    if(isAtEnd())
    {
        return errorToken("没有终点的字符串");
    }
    advance();
    return makeToken(TOKEN_STRING);
}

static Token character()
{
    while(!isAtEnd() && peek() != '\'')
    {
        if(peek() == '\n')
        {
            return errorToken("不支持多行字符");
        }
        advance();
    }
    if(isAtEnd())
    {
        return errorToken("此字符不完整，缺少右单引号！");
    }
    advance();
    int charLen = scanner.current - scanner.start -2;
    if (charLen == 1 || charLen == 0) {	// 单引号中一个字符或空字符
        return makeToken(TOKEN_CHARACTER);
    }
    // 需求: 现在单引号中间的字符序列长度超长了,于是要打印这个超长的序列
    // 输出效果是: 非单字符Token: xxx(单引号中间的序列)
    const char* charStart = scanner.start + 1; // 跳过第一个单引号
                                         // 我们需要格式化的向目标字符数组中输出一个字符串
    sprintf(meesage, "非单字符Token: %.*s", charLen, charStart);
    return errorToken(meesage);
}
// 处理无法识别的字符
static Token errorTokenWithChar(char character) {
    // 将无法识别的字符是什么输出
    sprintf(meesage, "Unexpected character: %c", character);
    return errorToken(meesage);
}

// Scanner词法分析器的核心逻辑,用于返回一个制作好的Token
Token scanToken() {
    // 跳过前置空白字符和注释
    skipWhitespace();
    // 记录下一个Token的起始位置
    scanner.start = scanner.current;

    // 如果curr指向了空字符,那么就已经处理源代码完毕了,直接返回TOKEN_EOF
    if (isAtEnd()) {
        return makeToken(TOKEN_EOF);
    }
    // 代码运行到这里,说明curr指向的不是空字符,那就继续处理
    /*
       (重点!!!!)
       调用advance函数,curr指针指向了此Token的第二个字符
       但是c仍然是Token的第一个字符!!!!
       */
    char c = advance();

    // 如果此Token的第一个字符是字母或下划线, 那么就进入标识符的处理模式
    // 同时关键字也合并在这里处理
    if (isAlpha(c)) {
        return identifier();
    }
    // 如果此Token的第一个字符是数字, 那就进入数字的处理模式
    if (isDigit(c)) {
        return number();
    }
    // 如果Token的第一个字符既不是数字也不是字母和下划线,那么说明此Token既不是标识符,也不是数字
    // 那就switch处理这个Token
    switch (c) {
        // 第一部分: 处理单字符Token
    case '(': return makeToken(TOKEN_LEFT_PAREN);
    case ')': return makeToken(TOKEN_RIGHT_PAREN);
    case '{': return makeToken(TOKEN_LEFT_BRACE);
    case '}': return makeToken(TOKEN_RIGHT_BRACE);
    case '[': return makeToken(TOKEN_LEFT_BRACKET);
    case ']': return makeToken(TOKEN_RIGHT_BRACKET);
    case ';': return makeToken(TOKEN_SEMICOLON);
    case ',': return makeToken(TOKEN_COMMA);
    case '.': return makeToken(TOKEN_DOT);
    case '~': return makeToken(TOKEN_TILDE);

              // ...TODO 还有比如{};,.

              // 第二部分: 可单可双字符的Token处理会稍微复杂一点,但不多
              // 如果Token的第一个字符是+号
    case '+':
              // curr指针当前已经指向了Token的第二个字符,也就是当前正在处理第二个字符
              // 如果Token的第二个字符也是+,那就生产++双字符Token ++返回
              if (match('+')) {
                  return makeToken(TOKEN_PLUS_PLUS);
              }
              else if (match('=')) {
                  return makeToken(TOKEN_PLUS_EQUAL);
              }
              else {
                  return makeToken(TOKEN_PLUS);
              }
    case '-':
              if (match('-'))
              {
                  return makeToken(TOKEN_MINUS_MINUS);
              }
              else if (match('+'))
              {
                  return makeToken(TOKEN_MINUS_EQUAL);
              }
              else
              {
                  return makeToken(TOKEN_MINUS);
              }
    case '*':
              return makeToken(match('=') ? TOKEN_STAR_EQUAL :
                               TOKEN_STAR);
    case '/':
              return makeToken(match('=') ? TOKEN_SLASH_EQUAL :
                               TOKEN_SLASH);
    case '%':
              return makeToken(match('=') ? TOKEN_PERCENT_EQUAL :
                               TOKEN_PERCENT);
    case '&':
              if (match('='))
              {
                  return makeToken(TOKEN_AMPER_EQUAL);
              }
              else if (match('&'))
              {
                  return makeToken(TOKEN_AMPER_AMPER);
              }
              return makeToken(TOKEN_AMPER);
    case '|':
              if (match('='))
              {
                  return makeToken(TOKEN_PIPE_EQUAL);
              }
              else if (match('&'))
              {
                  return makeToken(TOKEN_PIPE_PIPE);
              }
              return makeToken(TOKEN_PIPE);
    case '^':
              return makeToken(match('=') ? TOKEN_HAT_EQUAL : TOKEN_HAT);
    case '=':
              return makeToken(match('=') ? TOKEN_EQUAL_EQUAL : TOKEN_EQUAL);
    case '!':
              return makeToken(match('=') ? TOKEN_BANG_EQUAL : TOKEN_BANG);
    case '<':
              if (match('=')) {
                  return makeToken(TOKEN_LESS_EQUAL);
              }
              else if (match('<')) {
                  return makeToken(TOKEN_LESS_LESS);
              }
              else {
                  return makeToken(TOKEN_LESS);
              }
    case '>':
              if (match('=')) {
                  return makeToken(TOKEN_GREATER_EQUAL);
              }
              else if (match('>')) {
                  return makeToken(TOKEN_GREATER_GREATER);
              }
              else {
                  return makeToken(TOKEN_GREATER);
              }
              // ...TODO -,*,/
    case '"': return string(); // 如果Token的第一个字符是双引号,那就进入字符串的处理模式
    case '\'': return character();	// 如果Token的第一个字符是单引号,那就进入字符的处理模式
    }
    // 如果上述处理都没有处理成功,都没有生成合适的Token,说明该字符无法识别
    return errorTokenWithChar(c);
}


